import { compileDefinition, compileScript, getFunctionInfo, Scope } from "./script-compiler.js";
import { parseValue } from "./xml-value-parser.js";
import { Stage } from "./stage.js";

class Scene {
    constructor(project) {
        this.project = project;
        this.stage = null;
        this.functions = new Map();

        this.variables = {};

        this.varScope = new Scope();
        this.varScope.prefix = "env.globals.";
    }

    async loadXml(xml, shared) {
        if (xml.nodeName !== "scene") throw new Error("Expected <scene> tag");

        var blocksNode, stageNode, varsNode;

        for (let child of xml.children) {
            switch (child.nodeName) {
                case "stage": {
                    stageNode = child;
                    break;
                }

                case "blocks": {
                    blocksNode = child;
                    break;
                }

                case "variables": {
                    varsNode = child;
                    break;
                }
            }
        }
        
        // init var declarations
        var varNodes = [];

        for (let varNode of varsNode.children) {
            let varName = varNode.attributes.name.nodeValue;
            let safeVarName = this.varScope.addVariable(varName);

            this.variables[safeVarName] = 3;
            varNodes.push([varNode, safeVarName]);
        }

        console.log(this.variables);

        this.stage = new Stage(this);
        this.stage.loadXml(stageNode, shared);

        var paramTypes = new Map();

        // compile blocks and define variables after initializing stage
        // that is because global variables are defined in the stage

        // first get param types
        for (let blockNode of blocksNode.children) {
            let info = getFunctionInfo(blockNode);
            paramTypes.set(info.opcode, info.paramTypes);
        }

        console.log("param types");
        console.log(paramTypes);

        // compile blocks
        for (let blockNode of blocksNode.children) {
            console.group("block compilation");

            try {
                var def = compileDefinition(blockNode, this.stage.varScope, paramTypes);

                this.functions.set(def.header.opcode, {
                    paramTypes: def.header.paramTypes,
                    func: def.func,
                });
            } catch(err) {
                console.error(err);
            }

            console.groupEnd();
        }

        // then set variable values
        // do this last as variable values may have rings that use custom blocks
        console.log(varNodes);
        for (let [varNode, safeVarName] of varNodes) {
            // remove prefix
            safeVarName = safeVarName.slice(this.varScope.prefix.length);
            
            let valXml = varNode.firstElementChild;
            if (valXml) {
                let val = parseValue(valXml, this.varScope, shared.refs, this.functions);
                console.log(`set ${safeVarName} to ${val}`);
                this.variables[safeVarName] = val;
            } else {
                this.variables[safeVarName] = 0;
            }
        }
    }

    getFunction(fname) {
        return this.functions.get(fname);
    }
}

export { Scene }